{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "896fcdd8",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# 预处理数据\n",
    "\n",
    "# Load origin data\n",
    "import sys\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "data_file = './data.csv'  # 学生1-6年的近视数据\n",
    "data = pd.read_csv(data_file, encoding ='gbk')# 有中文用gbk，字符集用utf-8\n",
    "\n",
    "# 数据预处理\n",
    "# 丢弃前4列的数据，切勿重复执行，会一直裁剪数据\n",
    "data = data.iloc[:, 4:]# 前的：代表行，后面的代表列，：前后是代表从几行（列）到几行（列）\n",
    "#data.head(10)\n",
    "\n",
    "# 提取每一年的数据重新排列放入train_data.csv文件中\n",
    "data1 = data.loc[:,['1SE','1eye','1 0.5(K1+K2)','1 logMAR','2SE','2eye','2 0.5(K1+K2)','2 logMAR'\\\n",
    "                    ,'3SE','3eye','3 0.5(K1+K2)','3 logMAR','4SE','4eye','4 0.5(K1+K2)','4 logMAR'\\\n",
    "                   ,'5SE','5eye','5 0.5(K1+K2)','5 logMAR','6SE','6eye','6 0.5(K1+K2)','6 logMAR']]\n",
    "data1.to_csv('./train_data.csv')\n",
    "\n",
    "# 加载训练数据\n",
    "train_data = pd.read_csv('./train_data.csv')\n",
    "#train_data\n",
    "\n",
    "# 丢弃前1列的数据，切勿重复执行，会一直裁剪数据\n",
    "train_data = train_data.iloc[:, 1:]\n",
    "\n",
    "# 把dataframe转换成numpy数组，此时是65*24\n",
    "train_data = train_data.to_numpy()\n",
    "\n",
    "# 提取特征，放入x，y\n",
    "x = np.empty([66, 20], dtype = float)\n",
    "y = np.empty([66, 1], dtype = float)\n",
    "for people in range(66):\n",
    "    x[people, :] = train_data[people, 0:20]\n",
    "    y[people, :] = train_data[people, 23]\n",
    "    \n",
    "# 对x标准化# 求平均\n",
    "mean_x = np.mean(x, axis = 0) # 计算每一列的平均值（axis = 0），axis = 1则求每一行的平均值\n",
    "std_x = np.std(x, axis = 0) # 计算标准差\n",
    "\n",
    "for i in range(len(x)):\n",
    "    for j in range(len(x[0])):\n",
    "        if std_x[j] != 0:\n",
    "            x[i][j] = (x[i][j] - mean_x[j]) / std_x[j]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cd979ebc",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import torch\n",
    "import torchvision\n",
    "from torch.utils import data\n",
    "from d2l import torch as d2l\n",
    "\n",
    "# 获取数据\n",
    "# 注意，因为下面的load_array函数使用时需要的是tensor，所以需要将numpy转化为tensor（张量）\n",
    "\n",
    "x, y = torch.from_numpy(x), torch.from_numpy(y) # 如果x为tensor，则x.numpy()可以将其转换为numpy\n",
    "x, y = x.to(torch.float32), y.to(torch.float32) # 后面报错有double数据，在这先转换成float\n",
    "features, labels = x, y\n",
    "#features, labels"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a80c4c65",
   "metadata": {},
   "source": [
    "### TensorDataset\n",
    "* 对给定的tensor数据(样本和标签)，将它们包装成dataset。注意，如果是numpy的array，或者Pandas的DataFrame需要先转换成Tensor。\n",
    "\n",
    "### DataLoader\n",
    "* 数据加载器，组合数据集和采样器，并在数据集上提供单进程或多进程迭代器。它可以对我们上面所说的数据集Dataset作进一步的设置。\n",
    "\n",
    " * dataset (Dataset) – 加载数据的数据集。\n",
    " * batch_size (int, optional) – 每个batch加载多少个样本(默认: 1)。\n",
    " * shuffle (bool, optional) – 设置为True时会在每个epoch重新打乱数据(默认: true).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "10605a6a",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[tensor([[ 1.4382, -1.5466,  0.6574, -0.6361,  1.7838, -1.7062,  0.0893, -0.4214,\n",
       "           1.3749, -1.4546,  0.3289,  0.1843,  0.7413, -1.2023,  0.7522, -0.5151,\n",
       "           0.3343, -0.9373,  0.6789, -0.3107],\n",
       "         [-1.1210,  2.2952, -1.9382, -0.6361,  0.1223,  2.2480, -1.4918,  0.1206,\n",
       "          -0.1297,  2.1117, -1.6525, -0.0931,  0.0322,  1.7103, -1.8531, -0.1132,\n",
       "           0.1235,  1.7205, -2.0328, -0.3107],\n",
       "         [ 0.0423, -2.8362, -0.2771, -0.6361, -0.5898, -2.7194, -0.3419, -0.4214,\n",
       "           3.2557, -2.2801,  0.8692, -0.3335,  2.2381, -2.0530, -0.3941, -0.8268,\n",
       "           1.9504, -2.0217, -0.4684, -0.9944],\n",
       "         [ 0.1586,  1.2474, -1.3153, -0.6361,  0.4783,  0.9505, -1.2762, -0.4214,\n",
       "           0.0584,  0.9009, -1.2022,  0.1843,  0.1898,  0.9296, -1.2278,  0.4532,\n",
       "          -0.2278,  1.1677, -1.0941, -0.6957],\n",
       "         [-0.7720,  1.9325, -0.2771,  1.8743, -1.6579,  2.0009,  1.0236,  1.2623,\n",
       "          -1.8223,  2.0786,  1.0494,  3.4091, -2.0161,  1.9405, -0.1857,  2.3896,\n",
       "          -1.8439, -0.4164, -0.2598,  1.1597],\n",
       "         [-2.9822, -1.0227,  0.5535, -0.6361, -0.3524, -1.0636,  0.2330,  0.1206,\n",
       "          -0.1297, -0.9703, -1.2022, -0.7351,  0.1898, -1.0722,  0.4396, -0.1132,\n",
       "           0.6153, -1.0330,  0.5746, -0.6957]]),\n",
       " tensor([[ 0.5229],\n",
       "         [ 0.2218],\n",
       "         [ 0.0000],\n",
       "         [ 0.5229],\n",
       "         [ 0.6021],\n",
       "         [-0.1761]])]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 读取数据集\n",
    "\n",
    "def load_array(data_arrays, batch_size, is_train=True):  #@save\n",
    "    \"\"\"构造一个PyTorch数据迭代器。\"\"\"\n",
    "    dataset = data.TensorDataset(*data_arrays)\n",
    "    return data.DataLoader(dataset, batch_size, shuffle=is_train)\n",
    "\n",
    "batch_size = 6 # 30的loss值最低\n",
    "data_iter = load_array((features, labels), batch_size)\n",
    "\n",
    "next(iter(data_iter))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "05dd1685",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Sequential(\n",
       "  (0): Linear(in_features=20, out_features=1, bias=True)\n",
       ")"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# `nn` 是神经网络的缩写\n",
    "from torch import nn\n",
    "\n",
    "net = nn.Sequential(nn.Linear(20, 1))\n",
    "net"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c199e1f6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0.])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 初始化模型参数\n",
    "net[0].weight.data.normal_(0, 0.01)\n",
    "net[0].bias.data.fill_(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7087163d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义损失函数，MSELoss类，平方范数\n",
    "loss = nn.MSELoss()\n",
    "#loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "631ce622",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SGD (\n",
       "Parameter Group 0\n",
       "    dampening: 0\n",
       "    lr: 0.001\n",
       "    momentum: 0\n",
       "    nesterov: False\n",
       "    weight_decay: 0\n",
       ")"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义优化算法，小批量随机梯度下降算法\n",
    "# 实例化SGD实例\n",
    "trainer = torch.optim.SGD(net.parameters(), lr=0.001)\n",
    "trainer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "644c98b6",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 1, loss 0.250922\n",
      "epoch 2, loss 0.232078\n",
      "epoch 3, loss 0.216624\n",
      "epoch 4, loss 0.203512\n",
      "epoch 5, loss 0.192381\n",
      "epoch 6, loss 0.182704\n",
      "epoch 7, loss 0.174000\n",
      "epoch 8, loss 0.166265\n",
      "epoch 9, loss 0.159240\n",
      "epoch 10, loss 0.152822\n",
      "epoch 11, loss 0.146929\n",
      "epoch 12, loss 0.141456\n",
      "epoch 13, loss 0.136348\n",
      "epoch 14, loss 0.131535\n",
      "epoch 15, loss 0.127002\n",
      "epoch 16, loss 0.122740\n",
      "epoch 17, loss 0.118722\n",
      "epoch 18, loss 0.114905\n",
      "epoch 19, loss 0.111287\n",
      "epoch 20, loss 0.107842\n",
      "epoch 21, loss 0.104574\n",
      "epoch 22, loss 0.101465\n",
      "epoch 23, loss 0.098504\n",
      "epoch 24, loss 0.095672\n",
      "epoch 25, loss 0.092970\n",
      "epoch 26, loss 0.090400\n",
      "epoch 27, loss 0.087945\n",
      "epoch 28, loss 0.085600\n",
      "epoch 29, loss 0.083370\n",
      "epoch 30, loss 0.081225\n",
      "epoch 31, loss 0.079170\n",
      "epoch 32, loss 0.077211\n",
      "epoch 33, loss 0.075336\n",
      "epoch 34, loss 0.073535\n",
      "epoch 35, loss 0.071819\n",
      "epoch 36, loss 0.070173\n",
      "epoch 37, loss 0.068595\n",
      "epoch 38, loss 0.067092\n",
      "epoch 39, loss 0.065647\n",
      "epoch 40, loss 0.064267\n",
      "epoch 41, loss 0.062944\n",
      "epoch 42, loss 0.061678\n",
      "epoch 43, loss 0.060466\n",
      "epoch 44, loss 0.059304\n",
      "epoch 45, loss 0.058189\n",
      "epoch 46, loss 0.057122\n",
      "epoch 47, loss 0.056101\n",
      "epoch 48, loss 0.055121\n",
      "epoch 49, loss 0.054184\n",
      "epoch 50, loss 0.053284\n",
      "epoch 51, loss 0.052422\n",
      "epoch 52, loss 0.051596\n",
      "epoch 53, loss 0.050806\n",
      "epoch 54, loss 0.050048\n",
      "epoch 55, loss 0.049320\n",
      "epoch 56, loss 0.048623\n",
      "epoch 57, loss 0.047955\n",
      "epoch 58, loss 0.047314\n",
      "epoch 59, loss 0.046700\n",
      "epoch 60, loss 0.046110\n",
      "epoch 61, loss 0.045545\n",
      "epoch 62, loss 0.045002\n",
      "epoch 63, loss 0.044482\n",
      "epoch 64, loss 0.043984\n",
      "epoch 65, loss 0.043507\n",
      "epoch 66, loss 0.043047\n",
      "epoch 67, loss 0.042606\n",
      "epoch 68, loss 0.042184\n",
      "epoch 69, loss 0.041776\n",
      "epoch 70, loss 0.041386\n",
      "epoch 71, loss 0.041010\n",
      "epoch 72, loss 0.040651\n",
      "epoch 73, loss 0.040307\n",
      "epoch 74, loss 0.039975\n",
      "epoch 75, loss 0.039656\n",
      "epoch 76, loss 0.039349\n",
      "epoch 77, loss 0.039055\n",
      "epoch 78, loss 0.038772\n",
      "epoch 79, loss 0.038500\n",
      "epoch 80, loss 0.038237\n",
      "epoch 81, loss 0.037986\n",
      "epoch 82, loss 0.037745\n",
      "epoch 83, loss 0.037513\n",
      "epoch 84, loss 0.037290\n",
      "epoch 85, loss 0.037074\n",
      "epoch 86, loss 0.036867\n",
      "epoch 87, loss 0.036668\n",
      "epoch 88, loss 0.036477\n",
      "epoch 89, loss 0.036292\n",
      "epoch 90, loss 0.036114\n",
      "epoch 91, loss 0.035944\n",
      "epoch 92, loss 0.035779\n",
      "epoch 93, loss 0.035621\n",
      "epoch 94, loss 0.035468\n",
      "epoch 95, loss 0.035320\n",
      "epoch 96, loss 0.035178\n",
      "epoch 97, loss 0.035041\n",
      "epoch 98, loss 0.034909\n",
      "epoch 99, loss 0.034783\n",
      "epoch 100, loss 0.034661\n"
     ]
    }
   ],
   "source": [
    "# 训练模型\n",
    "# 这个功能就是用（数据量+批量-1）/批量 来求出循环次数。之所以减1是为了防止数据量为批量整数倍时结果出错\n",
    "num_epochs = 100\n",
    "for epoch in range(num_epochs):\n",
    "    # 从样本中获得x，y（读取数据那一部分来的）\n",
    "    for x, y in data_iter:\n",
    "        l = loss(net(x), y)\n",
    "        trainer.zero_grad()\n",
    "        l.backward()\n",
    "        trainer.step()\n",
    "    l = loss(net(features), labels)\n",
    "    #if(epoch%100==0):\n",
    "    #    print(f'epoch {epoch}, loss {l:f}')\n",
    "    print(f'epoch {epoch + 1}, loss {l:f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "42eb9a29",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[ 0.0563,  0.0035,  0.0039, -0.0125,  0.0044, -0.0223, -0.0002, -0.0045,\n",
       "          -0.0116,  0.0131,  0.0174, -0.0014, -0.0048,  0.0292,  0.0001,  0.0726,\n",
       "          -0.0876,  0.0318,  0.0101,  0.0959]]),\n",
       " tensor([0.3553]))"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w = net[0].weight.data\n",
    "b = net[0].bias.data\n",
    "w, b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5698b933",
   "metadata": {},
   "source": [
    "(tensor([[ 0.0171,  0.0074,  0.0184,  0.0019, -0.0013,  0.0082,  0.0019,  0.0024,\n",
    "          -0.0252,  0.0274, -0.0036,  0.0137, -0.0334,  0.0213,  0.0063,  0.0450,\n",
    "          -0.0391,  0.0118,  0.0030,  0.0491]]),\n",
    " tensor([0.1079]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "0af498b1",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 测试\n",
    "\n",
    "# 导入测试数据\n",
    "\n",
    "# 预处理\n",
    "#读取测试数据\n",
    "testdata_file = './test.csv'  # 测试学生1-6年的近视数据\n",
    "testdata = pd.read_csv(testdata_file)# , encoding ='gbk'有中文用gbk，字符集用utf-8\n",
    "#testdata.head(11)\n",
    "\n",
    "# 丢弃前4列的数据，切勿重复执行，会一直裁剪数据\n",
    "testdata = testdata.iloc[:, 4:]# 前的：代表行，后面的代表列，：前后是代表从几行（列）到几行（列）\n",
    "\n",
    "# 提取每一年的数据重新排列\n",
    "testdata1 = testdata.loc[:,['1SE','1eye','1 0.5(K1+K2)','1 logMAR','2SE','2eye','2 0.5(K1+K2)','2 logMAR'\\\n",
    "                    ,'3SE','3eye','3 0.5(K1+K2)','3 logMAR','4SE','4eye','4 0.5(K1+K2)','4 logMAR'\\\n",
    "                   ,'5SE','5eye','5 0.5(K1+K2)','5 logMAR','6SE','6eye','6 0.5(K1+K2)','6 logMAR']]\n",
    "\n",
    "# 将dataframe变成numpy数组\n",
    "test_data = testdata1\n",
    "test_data = test_data.to_numpy()\n",
    "\n",
    "# 将test数据也变成和训练数据一样的形式，取前五年的数据\n",
    "test_x = np.empty([10, 20], dtype = float)\n",
    "for t_people in range(10):\n",
    "    test_x[t_people, :] = test_data[t_people, 0:20]\n",
    "    \n",
    "# 数据标准化  \n",
    "for i in range(len(test_x)):\n",
    "    for j in range(len(test_x[0])):\n",
    "        if std_x[j] != 0:\n",
    "            test_x[i][j] = (test_x[i][j] - mean_x[j]) / std_x[j]\n",
    "\n",
    "# 将numpy变为tensor       \n",
    "test_x = torch.from_numpy(test_x)\n",
    "test_x = test_x.to(torch.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e3579459",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0.3136],\n",
       "        [0.1294],\n",
       "        [0.0283],\n",
       "        [0.2560],\n",
       "        [0.5326],\n",
       "        [0.3022],\n",
       "        [0.4568],\n",
       "        [0.8183],\n",
       "        [0.6570],\n",
       "        [0.6735]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# torch.matmul是tensor的乘法，输入可以是高维的。当输入是都是二维时，就是普通的矩阵乘法\n",
    "# 当输入有多维时，把多出的一维作为batch提出来，其他部分做矩阵乘法。\n",
    "ans_y = torch.matmul(test_x,w.T) + b\n",
    "ans_y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "56b60a75",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 画出预测值和实际值的图像\n",
    "real_y = test_data[:, 23]\n",
    "#real_y\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "fig = plt.figure()\n",
    "\n",
    "from itertools import chain\n",
    "x = np.arange(1, 11, 1)\n",
    "\n",
    "##使用库函数,把二维数组变为一维数组，方便画图\n",
    "y1 = list(chain.from_iterable(ans_y))\n",
    "\n",
    "plt.plot(x, y1, label='ans_y', marker = \"o\", color='r', linestyle='-')\n",
    "plt.plot(x, real_y, label='real_y', marker = \"x\", color='b', linestyle='-.')\n",
    "plt.xlabel(\"x\")\n",
    "plt.ylabel(\"y\")\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb15d0be",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
